package com.rtsapp.server.domain.utils;

import com.rtsapp.server.common.ByteBuffer;
import com.rtsapp.server.common.IByteBufferSerializable;
import com.rtsapp.server.domain.IEntity;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 嵌入的MapVO
 * 主要用于对实体的状态进行维护
 *
 *  TODO  使用 IByteBufferSerializable作为T类型的实现并未完成（它是关联实体的VO，构造方法一般有实体作为参数）
 */
public class EmbedMapProperty<K, V> {

    private final IEntity entity;

    private final ConcurrentMap<K, V> m = new ConcurrentHashMap<>();

    private final Class<K> keyClass;
    private final Class<V> valueClass;


    public EmbedMapProperty(IEntity entity, Class<K> keyClass, Class<V> valueClass) {
        this.entity = entity;
        this.keyClass = keyClass;
        this.valueClass = valueClass;
    }

    public int size() {
        return m.size();
    }

    public boolean containsKey(K key) {
        return m.containsKey(key);
    }

    public V get(K key) {
        return m.get(key);
    }

    public void clear() {
        if( m.isEmpty() ){
            return;
        }

        m.clear();
        entity.setDirty();
    }

    public V put(K key, V value) {
        V ret = m.put(key, value);
        entity.setDirty();
        return ret;
    }

    public<C extends K, T extends V> void putAll(EmbedMapProperty<C, T> m) {
        if (m == null) {
            return ;
        }

        for (C c : m.keySet()) {
            put(c, m.get(c));
        }
    }

    public V putIfAbsent(K key, V value) {

        V ret = m.putIfAbsent(key, value);
        entity.setDirty();

        return ret;
    }


    public V remove(K key) {
        V ret = m.remove(key);
        entity.setDirty();
        return ret;
    }

    public boolean isEmpty() {
        return m.isEmpty();
    }

    public Collection<V> values() {
        return m.values();
    }

    public Set<K> keySet() {
        return m.keySet();
    }

    public Set<Map.Entry<K, V>> entrySet() {
        return m.entrySet();
    }


    /**
     * 使用默认的方式序列化为bytes
     * @return
     */
    public byte[] getBytes() {

        ByteBuffer buffer = new ByteBuffer();

        buffer.writeInt(0);

        int count = 0; //由于多线程换将，map的大小可能再运行中被改变，因此用count来记数
        for (Iterator<Map.Entry<K, V>> it = entrySet().iterator(); it.hasNext(); ) {

            count++;

            Map.Entry<K, V> entry = it.next();

            K key = entry.getKey();
            V value = entry.getValue();

            if (key instanceof IByteBufferSerializable) {
                ((IByteBufferSerializable) (key)).writeToBuffer(buffer);
            } else {
                BytesSerializableUtils.write(buffer, key);
            }

            if (value instanceof IByteBufferSerializable) {
                ((IByteBufferSerializable) (value)).writeToBuffer(buffer);
            } else {
                BytesSerializableUtils.write(buffer, value);
            }
        }

        buffer.setInt(0, count);
        return buffer.toBytes();
    }

    /**
     * 使用默认的方式进行反序列化
     * @param bytes
     */
    public void setBytes(byte[] bytes) {

        this.clear();

        //bytes不能为空, 且长度大于整数
        if( bytes == null || bytes.length < 4 ){
            return;
        }

        try {
            ByteBuffer buffer = new ByteBuffer(bytes);
            int count = buffer.readInt();
            for (int i = 0; i < count; i++) {
                K key = null;
                if ( IByteBufferSerializable.class.isAssignableFrom(keyClass) ) {
                    key = (K) keyClass.newInstance();
                    ((IByteBufferSerializable)key).readFromBuffer( buffer );
                } else {
                    key = BytesSerializableUtils.read(buffer, keyClass);
                }

                V value = null;
                if ( IByteBufferSerializable.class.isAssignableFrom( valueClass ) ) {
                    value = (V) valueClass.newInstance();
                    ((IByteBufferSerializable)value).readFromBuffer( buffer );
                } else {
                    value = BytesSerializableUtils.read(buffer, valueClass);
                }

                this.put( key, value );
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


}
